package sdf;

import java.util.*;

import ptolemy.actor.sched.*;
import ptolemy.actor.*;
import ptolemy.kernel.*;
import ptolemy.kernel.util.*;
import ptolemy.domains.sdf.kernel.*;
import ptolemy.domains.sr.kernel.SRDirector;
import ptolemy.actor.util.DFUtilities;

import MoC.MoCVerifier;
import db.*;
import gui.ProgressDialog;
import SymbolTable.*;
import FlowGraph.*;
import FlowGraph.Nodes.*;
import util.*;
import MethodGraph.*;
import MoC.*;

//////////////////////////////////////////////////////////////////////////
//// SDFVerifier
/**


    * Schedulable : voce pode associar um director SDF que eh possivel escalonar
    a topologia apartir do dado nivel.
    
    * AtomicAsSDF : pode ser inserido em uma topologia SDF que vai se comportar
    como um ator atomico SDF. (SDF Analysis ou SR analysis e homo)

@author Ivan Jeukens
@version v 0.1 10/10/2002
*/
public class SDFVerifier implements MoCVerifier {

    public SDFVerifier() {
        _resultMessages = new ResultMessages("SDF Validation.");
    }

    ///////////////////////////////////////////////////////////////////
    ////                        public methods                     ////

    public String getName() {
        return "SDF";
    }

    // Analisa somente esse nivel da hierarquia
    public void analyze(CompositeActorInfo info, ProgressDialog progress) {
        CompositeActorResult compRes = info.getResult();
        if(compRes.isSpecific() && !compRes.getSpecificMoC().equals("SDF")) { 
            return;
        }

        boolean invalid = false;
        Iterator ite = info.atomicActorsIterator();
        while(ite.hasNext()) {
            AtomicActorInfo ainfo = (AtomicActorInfo) ite.next();
            ClassResult cres = ainfo.getResult();
            if(!cres.isValidFor("SDF")) {
                invalid = true;
                break;
            }
        }
        if(invalid) {
            return;
        }

        ite = info.compositeActorsIterator();
        while(ite.hasNext()) {
            CompositeActorInfo subinfo = (CompositeActorInfo) ite.next();
            CompositeActorResult res = subinfo.getResult();
            if(!res.isSDFSchedulable() && !res.isValidAsAtomicActorFor("SDF")) {
                invalid = true;
                break;
            }
        }
        if(invalid) {
            return;
        }

        invalid = _tryToSchedule(info);
        if(!invalid) {
            compRes.setSDFSchedulable();

            boolean isatomic = _isValidAsAtomic(info);
            if(isatomic) {
                compRes.addAssociatedDirector("SDF");
                compRes.setValidAsAtomicActorFor("SDF");

                StringBuffer buf = new StringBuffer(info.getName() + 
                    " valid as atomic for: SDF");
                SDFValidationMessage m = new SDFValidationMessage(info.getName(), 
                                Message.INFORMATION, Message.VALIDASATOMIC, buf.toString());
                ActorInstanceMessageLocation loc = new ActorInstanceMessageLocation(
                    MessageLocation.ACTOR_INSTANCE);
                loc.setActor(info.getEntity());
                m.setLocation(loc);
                _resultMessages.addMessage(m);

                if(_isHomogeneous(info)) {
                    compRes.setValidAsAtomicActorFor("SR");

                    buf = new StringBuffer(info.getName() + 
                        " valid as atomic for: SR");
                    m = new SDFValidationMessage(info.getName(), 
                                    Message.INFORMATION, Message.VALIDASATOMIC, buf.toString());
                    loc = new ActorInstanceMessageLocation(
                        MessageLocation.ACTOR_INSTANCE);
                    loc.setActor(info.getEntity());
                    m.setLocation(loc);
                    _resultMessages.addMessage(m);
                }
            }
            else {
                compRes.addAssociatedDirector("Controlable by SDF");
            }
        }
    }

    public void analyze(ClassInfo cinfo, ProgressDialog progress) {
        ClassResult cres = cinfo.getResult();
        if(cres.isSpecific() && !cres.getSpecificMoC().equals("SDF")) return;
        if(!cres.toVerify("SDF")) return;
        cres.removeToVerify("SDF");

        progress.addMessage("Performing SDF analysis on " + cinfo.getName(),1);

        _findSampleRates(cinfo);
    }

    public ResultMessages getResult() {
        return _resultMessages;
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private methods                    ////

    // Um ator sem portos (como o toplvel) eh considerado valido como atomico
    private boolean _isValidAsAtomic(CompositeActorInfo cinfo) {
        CompositeEntity top = cinfo.getEntity();
        Iterator pite = top.portList().iterator();
        while(pite.hasNext()) {
            IOPort p = (IOPort) pite.next();
            Iterator con = p.deepInsidePortList().iterator();
            while(con.hasNext()) {
                IOPort pc = (IOPort) con.next();

                if((p.isInput() && pc.isOutput()) ||
                   (p.isOutput() && pc.isInput())) continue;

                try {
                    int r = DFUtilities.getTokenConsumptionRate(p);
                    int r2 = DFUtilities.getTokenConsumptionRate(pc);

                    if(r == 0) {
                        DFUtilities.setTokenConsumptionRate(p, r2);
                    }
                    else
                    if(r != r2) {
                        return false;
                    }
                    
                    r = DFUtilities.getTokenProductionRate(p);
                    r2 = DFUtilities.getTokenProductionRate(pc);
                    
                    if(r == 0) {
                        DFUtilities.setTokenProductionRate(p, r2);
                    }
                    else
                    if(r != r2) {
                         return false;
                    }
                    
                    r = DFUtilities.getTokenInitProduction(p);
                    r2 = DFUtilities.getTokenInitProduction(pc);
                    
                    if(r == 0) {
                        DFUtilities.setTokenInitProduction(p, r2);
                    }
                    else
                    if(r != r2) {
                        return false;
                    }
                }
                catch(Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
        _printCompositeActorSampleRates(cinfo);

        return true;
    }

    // Tenta escalonar um ator composite com o diretor SDF.
    private boolean _tryToSchedule(CompositeActorInfo cinfo) {
        _setAtomicActorsPortsSampleRate(cinfo);
        _setSRDirectors(cinfo);

        CompositeEntity top = cinfo.getEntity();
        try {
            SDFDirector sdfdir = new SDFDirector(top, top.getName());
            sdfdir.preinitialize();
            // sdfdir.initialize();
        }
        catch(NotSchedulableException ex) {
            _notSchedulable(top.getFullName(), ex);
            _removeDirectors(cinfo);
            return true;
        }
        catch(IllegalActionException iaex) {
	        _notSchedulable(top.getFullName(), iaex);
            _removeDirectors(cinfo);
            return true;
        }
        catch(NameDuplicationException ndex) {
	        _notSchedulable(top.getFullName(), ndex);
            _removeDirectors(cinfo);
            return true;
        }
        _removeDirectors(cinfo);

        return false;
    }

    private void _removeDirectors(CompositeActorInfo info) {
        CompositeEntity ent = info.getEntity();
        Attribute a = ent.getAttribute(ent.getName());
        if(a != null && a instanceof Director) {
            try {
                a.setContainer(null);
            }
            catch(Exception ex) {
                ex.printStackTrace();
            }
        }
        Iterator ite = info.compositeActorsIterator();
        while(ite.hasNext()) {
            CompositeActorInfo subinfo = (CompositeActorInfo) ite.next();
            _removeDirectors(subinfo);
        }
    }

    // Para os atores atomicos de um nivel da topologia, seta as sample rates
    // calculadas. Uma vez associado uma sample rate ela nao eh mais removida.
    // Assim, quando niveis superiores forem testar o escalonamento, nao
    // eh necessario processar todas as subtopologias.
    private void _setAtomicActorsPortsSampleRate(CompositeActorInfo cinfo) {
        Iterator ite = cinfo.atomicActorsIterator();
        while(ite.hasNext()) {
            AtomicActorInfo ainfo = (AtomicActorInfo) ite.next();
            ClassResult res = ainfo.getResult();

            ComponentEntity ent = ainfo.getActor();
            List ports = ent.portList();
            Iterator pite = ports.iterator();
            while(pite.hasNext()) {
                IOPort p = (IOPort) pite.next();
                PortSampleRate psr = res.getPortSampleRate(p.getName());
                if(psr == null) continue;

                try {
                    if(psr.init.getMin(0) != 0) {
                        DFUtilities.setTokenInitProduction(p, psr.init.getMin(0));
                    }
                    if(psr.consumption.getMin(0) != 0) {
                        DFUtilities.setTokenConsumptionRate(p,
                                psr.consumption.getMin(0));
                    }
                    if(psr.production.getMin(0) != 0) {
                        DFUtilities.setTokenProductionRate(p,
                                psr.production.getMin(0));
                    }
                }
                catch(IllegalActionException ex) {
                        ex.printStackTrace();
                }
            }
        }
    }

    // Dado um nivel de topologia, verifica as subtopologias. Para as que
    // nao sao schedulable, verifica se sao validAsAtomic e associa um
    // diretor SR a elas.
    // Esse metodo so e executado quando nao houver subtopologias que nao
    // sao nem SDFSchedulable nem SDFAtomicActor.
    private void _setSRDirectors(CompositeActorInfo cinfo) {
        Iterator ite = cinfo.compositeActorsIterator();
        while(ite.hasNext()) {
            CompositeActorInfo subinfo = (CompositeActorInfo) ite.next();
            CompositeActorResult res = subinfo.getResult();

            if(res.isSDFSchedulable()) {
                _setSRDirectors(subinfo);
            }
            else
            // se nao for escalonavel, entao ele eh validAsAtomicForSDF so porque
            // eh tb SR e homogeneo. Entao, aqui tem que adicionar um SR director
            if(res.isValidAsAtomicActorFor("SR") && !res.isSDFSchedulable()) {
                // nesse caso, e SR e opaco
                CompositeEntity ent = subinfo.getEntity();
                try {
                    SRDirector srdir = new SRDirector(ent, ent.getName());
                }
                catch(Exception ex) {
                    ex.printStackTrace();
                }
            }
            else {
                // nao eh pra alcancar esse branch.

            }
        }
    }

    // No propagate, os portos ja estao com as devidas sample rates.
    private boolean _isHomogeneous(CompositeActorInfo cinfo) {
        CompositeEntity top = cinfo.getEntity();
        Iterator pite = top.portList().iterator();
        while(pite.hasNext()) {
            IOPort p = (IOPort) pite.next();

            try {
                if(p.isInput()) {
                    if(DFUtilities.getTokenConsumptionRate(p) > 1) return false;
                }

                if(p.isOutput()) {
                    if(DFUtilities.getTokenProductionRate(p) > 1) return false;
                }
            }
            catch(Exception ex) {
                ex.printStackTrace();
                return false;
            }
        }
        return true;
    }
    
    // ************************ SAMPLE RATES *********************************

    private void _findSampleRates(ClassInfo actor) {
        if(_DEBUG) {
            System.out.println("Find Sample Rates actor : " + actor.getName());
        }
        HashMap portssize = new HashMap();
        HashMap psr = new HashMap();

        ClassData root = actor.getRootClass();
        Iterator allports = root.getAllPorts();
        while(allports.hasNext()) {
            STPortEntry port = (STPortEntry) allports.next();
            PortSampleRate ps = new PortSampleRate(port.getName());
            psr.put(ps.getName(), ps);
            portssize.put(ps.getName(), new Integer(0));
            if(_DEBUG) {
                System.out.println("putting port " + ps.getName());
            }
        }
        MethodGraph initmg = null;
        MethodGraph prefiremg = null;
        MethodGraph firemg = null;
        MethodGraph postfiremg = null;

        LinkedList ll = root.getMethod("initialize");
        if(ll != null && ll.size() > 0) {
            STMethodEntry entry = (STMethodEntry) ll.get(0);
            FlowGraph fg = entry.getFlowGraph();
            initmg = fg.getMethodGraph();
            _findPortsSize(portssize, initmg);
        }

        ll = root.getMethod("prefire");
        if(ll != null && ll.size() > 0) {
            STMethodEntry entry = (STMethodEntry) ll.get(0);
            FlowGraph fg = entry.getFlowGraph();
            prefiremg = fg.getMethodGraph();
            _findPortsSize(portssize, prefiremg);
        }

        ll = root.getMethod("fire");
        if(ll != null && ll.size() > 0) {
            STMethodEntry entry = (STMethodEntry) ll.get(0);
            FlowGraph fg = entry.getFlowGraph();
            firemg = fg.getMethodGraph();
            _findPortsSize(portssize, firemg);
        }

        ll = root.getMethod("postfire");
        if(ll != null && ll.size() > 0) {
            STMethodEntry entry = (STMethodEntry) ll.get(0);
            FlowGraph fg = entry.getFlowGraph();
            postfiremg = fg.getMethodGraph();
            _findPortsSize(portssize, postfiremg);
        }

        _setPortsSize(psr, portssize);
        _resetPortsSampleRates(psr);

        if(_DEBUG) {
            System.out.println("Antes.");
            Iterator pite = psr.keySet().iterator();
            while(pite.hasNext()) {
                String pname = (String) pite.next();
                PortSampleRate ppsr = (PortSampleRate) psr.get(pname);
                System.out.println("Port : " + pname);
                System.out.println(ppsr);
            }
        }

        if(initmg != null) {
            if(_DEBUG) {
                System.out.println("Processando o init.");
            }
            MethodGraphNode source = initmg.getSource();
            MethodGraphNode sink = initmg.getSink();

            source.setPortsSampleRates(psr);
            _searchForComm(source, sink, true);
            _computeInputSampleRates(sink, false);

            psr = sink.getPortsSampleRates();
        }

        if(prefiremg != null) {
            if(_DEBUG) {
                System.out.println("Processando o prefire.");
            }
            MethodGraphNode source = prefiremg.getSource();
            MethodGraphNode sink = prefiremg.getSink();

            source.setPortsSampleRates(psr);
            _searchForComm(source, sink, false);

            if(_DEBUG) {
                System.out.println("Calculando o psr do sink no prefire");
            }
            _computeInputSampleRates(sink, false);

            psr = sink.getPortsSampleRates();
            if(_DEBUG) {
                System.out.println("PSR depois do prefire: " + psr);
            }
        }

        if(firemg != null) {
            if(_DEBUG) {
                System.out.println("Processando o fire.");
            }
            MethodGraphNode source = firemg.getSource();
            MethodGraphNode sink = firemg.getSink();

            source.setPortsSampleRates(psr);
            _searchForComm(source, sink, false);

            if(_DEBUG) {
                psr = sink.getPortsSampleRates();
                System.out.println("Sink psr before : " + psr);
            }
            _computeInputSampleRates(sink, false);
            psr = sink.getPortsSampleRates();

            if(_DEBUG) {
                System.out.println("Sink psr depois : " + psr);
            }
        }

        if(postfiremg != null) {
            if(_DEBUG) {
                System.out.println("Processando o postfire.");
            }
            MethodGraphNode source = postfiremg.getSource();
            MethodGraphNode sink = postfiremg.getSink();

            source.setPortsSampleRates(psr);
            _searchForComm(source, sink, false);

            _computeInputSampleRates(sink, false);
            psr = sink.getPortsSampleRates();
            if(_DEBUG) {
                System.out.println("Sink psr depois : " + psr);
            }
        }

        boolean invalid = false;
        Iterator ite = psr.values().iterator();
        while(ite.hasNext()) {
            int value = -1;
            LinkedList lines = new LinkedList();
            PortSampleRate sr = (PortSampleRate) ite.next();
            for(int i = 0;i < sr.init.minSize();i++) {
                if(sr.init.getMin(i) != sr.init.getMax(i)) {                    
                    _newMessage(Message.ERROR, Message.NONCONSTANTSAMPLERATE,
                       "Port " + sr.getName() + " does not have a constant " +
                       "initialization production sample rate. Has min = " +
                       sr.init.getMin(i) + " and max = " + sr.init.getMax(i),
                       lines, actor);
                    invalid = true;
                }

                if(value == -1) {
                    value = sr.init.getMin(i);
                }
                else
                if(value != sr.init.getMin(i)) {
                    _newMessage(Message.ERROR, Message.NONCONSTANTSAMPLERATE,
                       "Port " + sr.getName() + " does not have a constant " +
                       "initialization production sample rate on every channel.",
                       lines, actor);
                    invalid = true;
                }
            }

            value = -1;
            for(int i = 0;i < sr.production.minSize();i++) {
                if(sr.production.getMin(i) != sr.production.getMax(i)) {
                    _newMessage(Message.ERROR, Message.NONCONSTANTSAMPLERATE,
                       "Port " + sr.getName() + " does not have a constant " +
                       "production sample rate. Has min = " +
                       sr.production.getMin(i) + " and max = " + 
                       sr.production.getMax(i), lines, actor);
                    invalid = true;
                }

                if(value == -1) {
                    value = sr.production.getMin(i);
                }
                else 
                if(value != sr.production.getMin(i)) {
                    _newMessage(Message.ERROR, Message.NONCONSTANTSAMPLERATE,
                       "Port " + sr.getName() + " does not have a constant " +
                       "production sample rate on every channel.",
                       lines, actor);
                    invalid = true;
                }
            }

            value = -1;
            for(int i = 0;i < sr.consumption.minSize();i++) {
                if(sr.consumption.getMin(i) != sr.consumption.getMax(i)) {
                    _newMessage(Message.ERROR, Message.NONCONSTANTSAMPLERATE,
                       "Port " + sr.getName() + " does not have a constant " +
                       "consumption sample rate. Has min = " +
                       sr.consumption.getMin(i) + " and max = " + 
                       sr.consumption.getMax(i), lines, actor);
                    invalid = true;
                }

                if(value == -1) {
                    value = sr.consumption.getMin(i);
                }
                else
                if(value != sr.consumption.getMin(i)) {
                    _newMessage(Message.ERROR, Message.NONCONSTANTSAMPLERATE,
                       "Port " + sr.getName() + " does not have a constant " +
                       "consumption sample rate on every channel.",
                       lines, actor);
                    invalid = true;
                }
            }
        }
        if(_DEBUG) {
            System.out.println("Depois.");
            Iterator pite = psr.keySet().iterator();
            while(pite.hasNext()) {
                String pname = (String) pite.next();
                PortSampleRate ppsr = (PortSampleRate) psr.get(pname);
                System.out.println("Port : " + pname);
                System.out.println(ppsr);
            }
        }

        if(!invalid) {
            ClassResult cres = actor.getResult();
            cres.setPortsSampleRates(psr);
            cres.setValidFor("SDF");
            _printSampleRates(actor);
        }
    }

    private void _findPortsSize(HashMap portssize, MethodGraph mg) {
        LinkedList top = mg.getTopologicalOrder();
        for(int i = 0;i < top.size();i++) {
            MethodGraphNode mgnode = (MethodGraphNode) top.get(i);

            if(mgnode.getType() == MethodGraphNode.GRAPH_LINK) {
                MethodGraph link = mgnode.getMethodGraph();
                if(!mgnode.containsIOMethods()) continue;

                if( link.getFlowGraph() == mg.getFlowGraph() ) { // isso aqui na pode!
                    continue;
                }

                _findPortsSize(portssize, link);
                continue;
            }
            else
            if(mgnode.getType() == MethodGraphNode.SINK ||
               mgnode.getType() == MethodGraphNode.SOURCE ||
               mgnode.getType() == MethodGraphNode.LOOPHEAD) {
                continue;
            }

            String port = mgnode.getPort();
            int channel = mgnode.getChannel();

            //System.out.println("Find size Port = " + port);
            if(channel != MethodGraphNode.ALLCHANNELS) {
                Integer v = (Integer) portssize.get(port);
                //System.out.println("v = " + v);
                if(v.intValue() < channel) {
                    portssize.put(port, new Integer(channel));
                }
            }
        }
    }

    private void _setPortsSize(HashMap ports, HashMap portssize) {
        Iterator ite = ports.values().iterator();
        while(ite.hasNext()) {
            PortSampleRate psr = (PortSampleRate) ite.next();
            int size = ((Integer) portssize.get(psr.getName())).intValue();
            psr.setChannelSize(size + 1);
        }
    }

    private void _resetPortsSampleRates(HashMap ports) {
        Iterator ite = ports.values().iterator();
        while(ite.hasNext()) {
            PortSampleRate psr = (PortSampleRate) ite.next();
            psr.reset();
        }
    }

    private void _printSampleRates(ClassInfo cinfo) {
        String sum = new String("Sample Rates for " + cinfo.getName());
        SDFValidationMessage m = new SDFValidationMessage(cinfo.getName(),
            Message.INFORMATION, Message.SAMPLERATE, sum);

        StringBuffer buf = new StringBuffer();
        ClassResult cres = cinfo.getResult();
        Iterator psrite = cres.portsSampleRatesIterator();
        while(psrite.hasNext()) {
            PortSampleRate psr = (PortSampleRate) psrite.next();
            buf.append("port " + psr.getName() + " I=" + psr.init.getMin(0) +
              " P=" + psr.production.getMin(0) + " C=" + psr.consumption.getMin(0) +
              "\n");
        }
        m.setText(buf.toString());
        _resultMessages.addMessage(m);
    }
    
    private void _printCompositeActorSampleRates(CompositeActorInfo cinfo) {
        String sum = new String("Sample Rates for " + cinfo.getName());
        SDFValidationMessage m = new SDFValidationMessage(cinfo.getName(),
            Message.INFORMATION, Message.SAMPLERATE, sum);

        StringBuffer buf = new StringBuffer();
        CompositeEntity top = cinfo.getEntity();
        Iterator pite = top.portList().iterator();
        while(pite.hasNext()) {
            IOPort p = (IOPort) pite.next();
            try {
                buf.append("port " + p.getName() + " I=" + 
                    DFUtilities.getTokenInitProduction(p) +
                    " P= " + DFUtilities.getTokenProductionRate(p) +
                    " C= " + DFUtilities.getTokenConsumptionRate(p) + "\n");
            }
            catch(IllegalActionException ex) {
                ex.printStackTrace();
            }
        }
        m.setText(buf.toString());
        _resultMessages.addMessage(m);
    }

    private void _searchForComm(MethodGraphNode firstNode,
            MethodGraphNode stopNode, boolean isInit) {
        HashMap processed = new HashMap();
        LinkedList process = new LinkedList();
        process.add(firstNode);
        while(process.size() > 0) {
            MethodGraphNode mgnode = (MethodGraphNode) process.remove(0);

            if(processed.containsKey(mgnode) ||
               !_inputsHaveSampleRates(mgnode)) continue;

            processed.put(mgnode, null);
            switch(mgnode.getType()) {
                case MethodGraphNode.GRAPH_LINK: {
                    _computeInputSampleRates(mgnode, false);  // nao inclue o backedge

                    MethodGraph mg = mgnode.getMethodGraph();
                    if(mgnode.containsIOMethods()) {
                        MethodGraphNode source = mg.getSource();
                        MethodGraphNode sink = mg.getSink();

                        _resetPortsSampleRates(mg);
                        source.setPortsSampleRates(mgnode.getPortsSampleRates());

                        _searchForComm(source, sink, isInit);
                        _computeInputSampleRates(sink, false);
                        mgnode.setPortsSampleRates(sink.getPortsSampleRates());
                    }

                    for(int j = 0;j < mgnode.outputSize();j++) {
                        MethodGraphEdge edge = mgnode.getOutputEdge(j);
                        if(edge.isSDFDead()) continue;

                        MethodGraphNode outn = edge.getTo();
                        if(outn == stopNode) continue;
                        process.add(outn);
                    }
                } break;

                case MethodGraphNode.LOOPHEAD: {
                    FlowGraphNode fgn = mgnode.getLoopHead();

                    if(fgn.getType() == FlowGraphNode.DO_BEGIN) {
                        _computeInputSampleRates(mgnode, false);
                        _updateSampleRates(mgnode);

                        for(int j = 0;j < mgnode.outputSize();j++) {
                            MethodGraphEdge edge = mgnode.getOutputEdge(j);
                            if(edge.isBack()) continue;
                            MethodGraphNode outn = edge.getTo();
                            if(outn == stopNode) continue;
                            process.add(outn);
                        }
                    }
                    else {
                        _computeInputSampleRates(mgnode, false);  // nao inclue o backedge

                        if(mgnode.containsIOMethods()) {
                            FlowGraphNode fgnode = mgnode.getLoopHead();
                            if(_DEBUG) {
                                System.out.println("Processando loop " + 
                                    fgnode.getAbsTreeNode().toStringTree());
                            }

                            Iterator ite = mgnode.firstLoopNodesIterator();
                            while(ite.hasNext()) {
                                MethodGraphNode fln = (MethodGraphNode) ite.next();
                                if(_DEBUG) {
                                    System.out.println("Primeiro no cabeca " + fln);
                                }
                                _searchForComm(fln, mgnode, isInit);
                            }

                            mgnode.setPortsSampleRates(null);
                            _computeInputSampleRates(mgnode, true);   // so backedge
                        }

                        Iterator ite = mgnode.continueLoopNodesIterator();
                        while(ite.hasNext()) {
                            MethodGraphNode cln = (MethodGraphNode) ite.next();
                            if(cln == stopNode) continue;
                            process.add(cln);
                        }
                    }
                } break;

                default: {
                    _computeInputSampleRates(mgnode, false);
                    if(isInit) {
                        _updateInitSampleRates(mgnode);
                    }
                    else {
                        _updateSampleRates(mgnode);
                    }
                    for(int j = 0;j < mgnode.outputSize();j++) {
                        MethodGraphEdge edge = mgnode.getOutputEdge(j);
                        if(edge.isSDFDead()) continue;

                        MethodGraphNode outn = edge.getTo();
                        if(outn == stopNode) continue;
                        process.add(outn);
                    }
                }
            }
        }
    }

    private void _resetPortsSampleRates(MethodGraph mg) {
        Iterator ite = mg.nodesIterator();
        while(ite.hasNext()) {
            MethodGraphNode node = (MethodGraphNode) ite.next();
            node.setPortsSampleRates(null);
        }
    }

    private void _computeInputSampleRates(MethodGraphNode mgnode,
            boolean withBackEdge) {
        if(_DEBUG) {
            System.out.println("computeInput de " + mgnode);
            System.out.println("Input size = " + mgnode.inputSize());
        }

        HashMap psr = null;
        for(int j = 0;j < mgnode.inputSize();j++) {
            MethodGraphEdge edge = mgnode.getInputEdge(j);

            if(edge.isSDFDead()) continue;

            if( (withBackEdge && !edge.isBack()) ||
                (!withBackEdge && edge.isBack()) ) continue;

            MethodGraphNode inmgnode = edge.getFrom();
            if(_DEBUG) { 
                System.out.println("Input : " + inmgnode);
                System.out.println("PSR = " + inmgnode.getPortsSampleRates());
            }

            if(mgnode.getPortsSampleRates() == null) {
                mgnode.setPortsSampleRates(
                    _copySampleRates(inmgnode.getPortsSampleRates()));
                continue;
            }
            else {
                psr = mgnode.getPortsSampleRates();
            }

            HashMap ps = inmgnode.getPortsSampleRates();

            Iterator ite = psr.values().iterator();
            while(ite.hasNext()) {
                PortSampleRate s = (PortSampleRate) ite.next();
                PortSampleRate ins = (PortSampleRate) ps.get(s.getName());
                s.updateInit(ins);
                s.updateProduction(ins);
                s.updateConsumption(ins);
            }
        }
    }

    private boolean _inputsHaveSampleRates(MethodGraphNode mgnode) {
        for(int j = 0;j < mgnode.inputSize();j++) {
            MethodGraphEdge edge = mgnode.getInputEdge(j);
            if(edge.isBack() || edge.isSDFDead()) continue;
            MethodGraphNode inmgnode = edge.getFrom();

            HashMap ps = inmgnode.getPortsSampleRates();
            if(ps == null) return false;
        }
        return true;
    }

    private void _updateInitSampleRates(MethodGraphNode mgnode) {
        String port = mgnode.getPort();
        HashMap map = mgnode.getPortsSampleRates();
        PortSampleRate psr = (PortSampleRate) map.get(port);

        int channel = mgnode.getChannel();
        int ntokens = mgnode.getNTokens();
        switch(mgnode.getType()) {
            case MethodGraphNode.SEND:
            case MethodGraphNode.SENDARRAY: {
                if(channel == MethodGraphNode.ALLCHANNELS) {
                    for(int j = 0;j < psr.init.minSize();j++) {
                        psr.init.incrMin(j, ntokens);
                        psr.init.incrMax(j, ntokens);
                    }
                }
                else {
                    psr.init.incrMin(channel, ntokens);
                    psr.init.incrMax(channel, ntokens);
                }
            } break;

            case MethodGraphNode.BROADCAST:
            case MethodGraphNode.BROADCASTARRAY: {
                for(int j = 0;j < psr.init.minSize();j++) {
                    psr.init.incrMin(j, ntokens);
                    psr.init.incrMax(j, ntokens);
                }
            } break;

            case MethodGraphNode.GET:
            case MethodGraphNode.GETARRAY: {
                System.out.println("ERROR ! cannot have get within " + 
                   " initialize method!!!!");
            } break;

            case MethodGraphNode.GRAPH_LINK: {
                System.out.println("TEM UM GRAPH LINK NO UPDATESAMPLE!!!");
            } break;
        }
    }

    private void _updateSampleRates(MethodGraphNode mgnode) {
        if(_DEBUG) {
            System.out.println("update sr rates em " + mgnode);
            if(mgnode.getType() == MethodGraphNode.SOURCE) {
                System.out.println("E o SOURCE");
            }
        }
        String port = mgnode.getPort();

        if(_DEBUG) {
            System.out.println("port = " + port);
        }

        HashMap map = mgnode.getPortsSampleRates();
        if(_DEBUG) {
            System.out.println("Map = " + map);
        }
        PortSampleRate psr = (PortSampleRate) map.get(port);

        if(_DEBUG) {
            System.out.println("psr = " + psr);
        }
        int channel = mgnode.getChannel();
        int ntokens = mgnode.getNTokens();
        switch(mgnode.getType()) {
            case MethodGraphNode.SEND:
            case MethodGraphNode.SENDARRAY: {                
                if(channel == MethodGraphNode.ALLCHANNELS) {
                    for(int j = 0;j < psr.init.minSize();j++) {
                        psr.production.incrMin(j, ntokens);
                        psr.production.incrMax(j, ntokens);
                    }
                }
                else {
                    psr.production.incrMin(channel, ntokens);
                    psr.production.incrMax(channel, ntokens);
                }
            } break;

            case MethodGraphNode.BROADCAST:
            case MethodGraphNode.BROADCASTARRAY: {
                for(int j = 0;j < psr.init.minSize();j++) {
                    psr.production.incrMin(j, ntokens);
                    psr.production.incrMax(j, ntokens);
                }
            } break;

            case MethodGraphNode.GET:
            case MethodGraphNode.GETARRAY: {
                if(channel == MethodGraphNode.ALLCHANNELS) {
                    for(int j = 0;j < psr.init.minSize();j++) {
                        psr.consumption.incrMin(j, ntokens);
                        psr.consumption.incrMax(j, ntokens);
                    }
                }
                else {
                    psr.consumption.incrMin(channel, ntokens);
                    psr.consumption.incrMax(channel, ntokens);
                }
            } break;

            case MethodGraphNode.GRAPH_LINK: {
                System.out.println("TEM UM GRAPH LINK NO UPDATESAMPLE!!!");
            } break;
        }
    }

    private HashMap _copySampleRates(HashMap sr) {
        HashMap ret = new HashMap();
        Iterator ite = sr.values().iterator();
        while(ite.hasNext()) {
            PortSampleRate psr = (PortSampleRate) ite.next();
            PortSampleRate clone = (PortSampleRate) psr.clone();
            ret.put(clone.getName(), clone);
        }
        return ret;
    }

    private void _notSchedulable(String name, Exception ex) {
        String sum = new String(name + " topology cannot be scheduled.");
        SDFValidationMessage m = new SDFValidationMessage(name, Message.ERROR,
            Message.NOTSCHEDULABLE, sum);
        m.setText(ex.toString());
        _resultMessages.addMessage(m);

        ex.printStackTrace();
    }

    private Message _newMessage(int status, int code, String text,
            LinkedList lines, ClassInfo cinfo) {
        SDFValidationMessage m = new SDFValidationMessage(cinfo.getName(), 
            status, code, text);
        ClassMessageLocation er = new ClassMessageLocation(
            MessageLocation.CLASS_SOURCE);
        for(int i = 0;i < lines.size();i++) {
            er.addLine((Line)lines.get(i));
        }
        er.setClassInfo(cinfo);
        m.setLocation(er);
        _resultMessages.addMessage(m);
        return m;
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private variables                  ////

    private ResultMessages _resultMessages;
    
    private static final boolean _DEBUG = false;
}
